﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using l1cs.server.model.map;
using l1cs.server.datatables;
using l1cs.server.model;
using l1cs.server.model.trap;
using l1cs.server.server.datatables;
using l1cs.server.model.gametime;
using System.Threading;
using l1cs.server.model.Instance;
using l1cs.server.utils;

namespace l1cs.server
{
    public class GameServer
    {
        private TcpListener _serverSocket;
        private static Logger _log = Logger.getLogger( typeof( GameServer ).FullName );
        private int _port;
        // private Logins _logins;
        private LoginController _loginController;
        //private int chatlvl;

        public void threadSub()
        {
            Console.WriteLine( String.Format( "利用メモリ: {0:f2}MB", GC.GetTotalMemory( false ) / 1024.0 / 1024.0 ) );
            Console.WriteLine( "クライアント接続待機中..." );
            while ( true )
            {
                try
                {
                    Socket socket = _serverSocket.AcceptSocket();
                    IPEndPoint endpoint = (IPEndPoint)socket.RemoteEndPoint;
                    IPAddress address = endpoint.Address;
                    Console.WriteLine( "接続試行中IP " + address.ToString() );
                    IPHostEntry hostInfo = Dns.GetHostEntry( address );
                    String host = hostInfo.HostName;
                    if ( IpTable.getInstance().isBannedIp( host ) )
                    {
                        _log.info( "banned IP(" + host + ")" );
                    }
                    else
                    {
                        ClientThread client = new ClientThread( socket, address, hostInfo );
                        ThreadPool.QueueUserWorkItem( new WaitCallback( client.threadSub ) );
                    }
                }
                catch ( SocketException ex )
                {
                    _log.info( "ソケットエラーが発生しました。\n" + ex.StackTrace );
                }
            }
        }

        private static GameServer _instance;

        private GameServer() { }

        public static GameServer getInstance()
        {
            if ( _instance == null )
            {
                _instance = new GameServer();
            }
            return _instance;
        }

        public Thread initialize()
        {
            String s = Config.GAME_SERVER_HOST_NAME;
            _port = Config.GAME_SERVER_PORT;
            IPAddress ipAdd;
            if ( !"*".Equals( s ) )
                ipAdd = IPAddress.Parse( s );
            else ipAdd = IPAddress.Parse( "127.0.0.1" );
            _serverSocket = new TcpListener( ipAdd, _port );
            try
            {
                _serverSocket.Start();
            }
            catch ( SocketException ex )
            {
                _log.info( ex.Message + "\n" + ex.StackTrace );
                return null;
            }
            Console.WriteLine( "サーバーセッティング: サーバーソケット生成" );
            IdFactory.getInstance();
            L1WorldMap.getInstance();
            _loginController = LoginController.getInstance();
            _loginController.setMaxAllowedOnlinePlayers( Config.MAX_ONLINE_USERS );

            // 全キャラクターネームロード
            CharacterTable.getInstance().loadAllCharName();

            // オンライン状態リセット
            CharacterTable.clearOnlineStatus();

            //// ゲーム時間時計
            L1GameTimeClock.init();

            // UBタイムコントローラー
            UbTimeController ubTimeContoroller = UbTimeController.getInstance();
            ThreadPool.QueueUserWorkItem( new WaitCallback( ubTimeContoroller.threadSub ) );

            // 戦争タイムコントローラー
            WarTimeController warTimeController = WarTimeController.getInstance();
            //GeneralThreadPool.getInstance().execute(warTimeController);
            ThreadPool.QueueUserWorkItem( new WaitCallback( warTimeController.threadSub ) );

            // 精霊の石生成
            if ( Config.ELEMENTAL_STONE_AMOUNT > 0 )
            {
                ElementalStoneGenerator elementalStoneGenerator = ElementalStoneGenerator.getInstance();
                ThreadPool.QueueUserWorkItem( new WaitCallback( elementalStoneGenerator.threadSub ) );
            }

            // ホームタウン
            HomeTownTimeController.getInstance();

            // アジト競売タイムコントローラー
            AuctionTimeController auctionTimeController = AuctionTimeController.getInstance();
            ThreadPool.QueueUserWorkItem( new WaitCallback( auctionTimeController.threadSub ) );

            // アジト税金タイムコントローラー
            HouseTaxTimeController houseTaxTimeController = HouseTaxTimeController.getInstance();
            ThreadPool.QueueUserWorkItem( new WaitCallback( houseTaxTimeController.threadSub ) );

            // 釣りタイムコントローラー
            FishingTimeController fishingTimeController = FishingTimeController.getInstance();
            ThreadPool.QueueUserWorkItem( new WaitCallback( fishingTimeController.threadSub ) );

            // NPCチャットタイムコントローラー
            NpcChatTimeController npcChatTimeController = NpcChatTimeController.getInstance();
            ThreadPool.QueueUserWorkItem( new WaitCallback( npcChatTimeController.threadSub ) );

            // ライトタイムコントローラー
            LightTimeController lightTimeController = LightTimeController.getInstance();
            ThreadPool.QueueUserWorkItem( new WaitCallback( lightTimeController.threadSub ) );

            Announcements.getInstance();
            NpcTable.getInstance();
            L1DeleteItemOnGround deleteitem = new L1DeleteItemOnGround();
            deleteitem.initialize();

            SpawnTable.getInstance();
            MobGroupTable.getInstance();
            SkillsTable.getInstance();
            PolyTable.getInstance();
            ItemTable.getInstance();
            DropTable.getInstance();
            DropItemTable.getInstance();
            ShopTable.getInstance();
            NPCTalkDataTable.getInstance();
            L1World.getInstance();
            L1WorldTraps.getInstance();
            Dungeon.getInstance();
            NpcSpawnTable.getInstance();
            IpTable.getInstance();
            MapsTable.getInstance();
            UBSpawnTable.getInstance();
            PetTable.getInstance();
            ClanTable.getInstance();
            CastleTable.getInstance();
            L1CastleLocation.setCastleTaxRate(); // これはCastleTable初期化後でなければいけない
            GetBackRestartTable.getInstance();
            DoorSpawnTable.getInstance();
            //GeneralThreadPool.getInstance();
            L1NpcRegenerationTimer.getInstance();
            ChatLogTable.getInstance();
            WeaponSkillTable.getInstance();
            NpcActionTable.getInstance(); // TODO 優先度2 XML 一応作成
            GMCommandsConfig.load(); // TODO 優先度2 XML 一応作成
            Getback.loadGetBack();
            PetTypeTable.getInstance();
            L1BossCycle.getInstance();// TODO 優先度2 XML 一応作成
            L1TreasureBox.getInstance(); // TODO 優先度2 XML 一応作成
            SprTable.getInstance();
            ResolventTable.getInstance();
            FurnitureSpawnTable.getInstance();
            NpcChatTable.getInstance();
            MailTable.getInstance();

            Console.WriteLine( "ローディング完了" );
            //Runtime.getRuntime().addShutdownHook(Shutdown.getInstance());

#if DEBUG
            //new TestClass();
            L1Random.getInt( 1 );
#endif
            Thread thread = new Thread( new ThreadStart( threadSub ) );
            thread.Name = "GameServer";
            thread.IsBackground = true;
            thread.Start();
            return thread;
        }

        public void socketClose()
        {
            if ( _serverSocket != null )
            {
                _serverSocket.Stop();
            }
        }
        /**
         * オンライン中のプレイヤー全てに対してkick、キャラクター情報の保存をする。
         */
        public void disconnectAllCharacters()
        {
            L1PcInstance[] players = L1World.getInstance().getAllPlayers();
            foreach ( L1PcInstance pc in players )
            {
                pc.getNetConnection().setActiveChar( null );
                pc.getNetConnection().kick();
            }
            // 全員Kickした後に保存処理をする
            foreach ( L1PcInstance pc in players )
            {
                ClientThread.quitGame( pc );
                L1World.getInstance().removeObject( pc );
            }
        }

        //private class ServerShutdownThread extends Thread {
        private int secondsCount;

        //public ServerShutdownThread(int secondsCount) {
        //    _secondsCount = secondsCount;
        //}

        public void ServerShutdownThreadSub( object state )
        {
            L1World world = L1World.getInstance();
            //int secondsCount = _secondsCount;
            world.broadcastServerMessage( "ただいまより、サーバーをシャットダウンします。" );
            world.broadcastServerMessage( "安全な場所でログアウトしてください" );
            while ( 0 < secondsCount )
            {
                if ( secondsCount <= 30 )
                {
                    world.broadcastServerMessage( "ゲームが" + secondsCount + "秒後にシャットダウンします。ゲームを中断してください。" );
                }
                else
                {
                    if ( secondsCount % 60 == 0 )
                    {
                        world.broadcastServerMessage( "ゲームが" + secondsCount / 60 + "分後にシャットダウンします。" );
                    }
                }
                Thread.Sleep( 1000 );
                secondsCount--;
            }
            shutdown();
        }
        //}

        private Timer _shutdownThread = null;

        public void shutdownWithCountdown( int secondsCount )
        {
            lock ( this )
            {
                if ( _shutdownThread != null )
                {
                    // 既にシャットダウン要求が行われている
                    return;
                }
                // TODO 優先度9 タイマースレッド
                //_shutdownThread = new ServerShutdownThread( secondsCount );
                //GeneralThreadPool.getInstance().execute( _shutdownThread );
                this.secondsCount = secondsCount;
                _shutdownThread = new Timer( new TimerCallback( ServerShutdownThreadSub ), null, 0, Timeout.Infinite );
            }
        }

        public void shutdown()
        {
            disconnectAllCharacters();
            //System.exit( 0 );
        }

        public void abortShutdown()
        {
            lock ( this )
            {
                if ( _shutdownThread == null )
                {
                    // シャットダウン要求が行われていない
                    return;
                }
                // TODO 優先度9 タイマースレッド
                //_shutdownThread.interrupt();
                //_shutdownThread = null;
                _shutdownThread.Dispose();
                _shutdownThread = null;
            }
        }
    }
}